Skip to content

3.x: handle synchronous request retry failures#943

Merged
dkropachev merged 1 commit into
scylla-3.xfrom
issue-941-requesthandler-sync-retry
Jul 3, 2026
Merged

3.x: handle synchronous request retry failures#943
dkropachev merged 1 commit into
scylla-3.xfrom
issue-941-requesthandler-sync-retry

Conversation

@dkropachev

@dkropachev dkropachev commented Jul 2, 2026

Copy link
Copy Markdown

Fixes #941
Part of #939

RequestHandler can see request-path transport failures before the normal asynchronous callback retry path is installed.

Changes:

  • Treat synchronous ConnectionException/BusyConnectionException from HostConnectionPool.borrowConnection(...) as failed host attempts so the query plan can continue to another host.
  • Handle synchronous failures while sending the internal PREPARE after an UNPREPARED response by releasing the connection, recording the host error, and continuing through retry/no-host handling.
  • Handle unexpected runtime failures in that reprepare write path the same way.
  • Add regression coverage for synchronous borrow failure failover and defunct-connection failure during UNPREPARED reprepare.

Testing:

  • Not run in this pass (rebase/squash/metadata update only).

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@dkropachev, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 11 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: cbda8067-caa5-41e4-abb3-72adf428e8da

📥 Commits

Reviewing files that changed from the base of the PR and between 74fe249 and efe29bf.

📒 Files selected for processing (3)
  • driver-core/src/main/java/com/datastax/driver/core/Connection.java
  • driver-core/src/main/java/com/datastax/driver/core/RequestHandler.java
  • driver-core/src/test/java/com/datastax/driver/core/RequestHandlerTest.java
📝 Walkthrough

Walkthrough

This PR updates RequestHandler.SpeculativeExecution to catch synchronous ConnectionException and BusyConnectionException failures during host borrowing and UNPREPARED recovery. Borrow failures now log and continue to the next host. UNPREPARED prepare-write failures now release or defunct the connection, log the error, and retry instead of failing immediately. Connection also gains a test-only defunct marker, and RequestHandlerTest adds coverage for next-host retry and NoHostAvailableException reporting.

Sequence Diagram(s)

sequenceDiagram
  participant RequestHandler
  participant HostConnectionPool
  participant Connection
  participant NextHost

  RequestHandler->>HostConnectionPool: borrowConnection(...)
  HostConnectionPool-->>RequestHandler: ConnectionException / BusyConnectionException
  RequestHandler->>RequestHandler: logError(...)
  RequestHandler->>NextHost: try next host

  RequestHandler->>Connection: write(prepareAndRetry(...))
  Connection-->>RequestHandler: ConnectionException / BusyConnectionException / RuntimeException
  RequestHandler->>Connection: release() / release(true)
  RequestHandler->>RequestHandler: retry(false, null)
Loading

Possibly related issues

Possibly related PRs

  • scylladb/java-driver#942: Related changes around HostConnectionPool.borrowConnection(...) failure handling in request execution flow.

Suggested reviewers: roydahan

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: handling synchronous request retry failures.
Description check ✅ Passed The description clearly matches the changeset and describes the retry-failure fixes and tests.
Linked Issues check ✅ Passed The changes address synchronous borrowConnection and UNPREPARED prepare-write failures, avoid retrying runtime errors, and add the required regression tests.
Out of Scope Changes check ✅ Passed The test helper additions and small priming import cleanup are in service of the linked retry-failure fixes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@dkropachev

Copy link
Copy Markdown
Author

@coderabbitai review

@dkropachev
dkropachev requested a review from Copilot July 2, 2026 19:32
@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the driver-core request retry internals to correctly handle synchronous transport failures that can occur before the existing async callback-based retry/failover logic is installed (notably during connection borrow and UNPREPARED re-prepare).

Changes:

  • Treat synchronous ConnectionException / BusyConnectionException thrown during HostConnectionPool.borrowConnection(...) as a host failure and continue the query plan to the next host.
  • Handle synchronous connection/busy failures when sending the internal PREPARE after an UNPREPARED response by recording the host error and retrying via the normal next-host/no-host path.
  • Add regression tests covering (1) synchronous borrow failure failover to the next host and (2) synchronous defunct-connection failure during UNPREPARED re-prepare resulting in NoHostAvailableException with per-host errors.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
driver-core/src/main/java/com/datastax/driver/core/RequestHandler.java Catches sync connection/busy failures during borrow and UNPREPARED re-prepare write, logging host errors and allowing the query plan to continue.
driver-core/src/test/java/com/datastax/driver/core/RequestHandlerTest.java Adds regression coverage for sync borrow failure failover and sync PREPARE-write failure during UNPREPARED recovery.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@dkropachev
dkropachev force-pushed the issue-941-requesthandler-sync-retry branch from b157fd6 to 6ed5c98 Compare July 3, 2026 03:09
} catch (RuntimeException e) {
targetKeyspace.compareAndSet(attempt, existingAttempt);
ksFuture.setException(e);
throw e;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to throw? Why can't we just return future?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in the current branch: setKeyspaceAsync now restores the target keyspace state, completes ksFuture exceptionally, and returns that future when write(...) fails instead of rethrowing.

@dkropachev
dkropachev force-pushed the issue-941-requesthandler-sync-retry branch from 6ed5c98 to 16f5c34 Compare July 3, 2026 15:30
@coderabbitai
coderabbitai Bot requested a review from roydahan July 3, 2026 15:31
@dkropachev
dkropachev force-pushed the issue-941-requesthandler-sync-retry branch from 16f5c34 to 75888d3 Compare July 3, 2026 15:35
@dkropachev dkropachev changed the title 3.x: Handle synchronous request retry failures 3.x: handle synchronous request retry failures Jul 3, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@driver-core/src/main/java/com/datastax/driver/core/RequestHandler.java`:
- Around line 432-438: The borrow-connection failure handling in RequestHandler
should keep the connection error metric consistent across sync and async paths.
In the `borrowConnection` async `onFailure` callback, add the same
`metrics().getErrorMetrics().getConnectionErrors().inc()` update that the
`ConnectionException` catch block already performs, using the same
`metricsEnabled()` guard and existing logging/retry flow. This should be applied
in the `RequestHandler` failure handling code around the `borrowConnection`
callback so `connectionErrors` is incremented regardless of timing.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 70c76a0a-9f79-4f79-a3be-a01e1fcfa673

📥 Commits

Reviewing files that changed from the base of the PR and between 7da1f87 and 16f5c34.

📒 Files selected for processing (2)
  • driver-core/src/main/java/com/datastax/driver/core/RequestHandler.java
  • driver-core/src/test/java/com/datastax/driver/core/RequestHandlerTest.java

Comment thread driver-core/src/main/java/com/datastax/driver/core/RequestHandler.java Outdated

@nikagra nikagra left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the two production-code changes and the test additions.

Production code (RequestHandler.java)

The core fix in the UNPREPARED reprepare path is correct. Before this PR, if write(connection, prepareAndRetry(...)) threw synchronously (e.g., ConnectionException("Write attempt on defunct connection")), the outer catch (Exception e) at the bottom of onSet would catch it but never call connection.release(), leaking the connection. The new inner try-catch fixes that.

Left two inline comments below.

Test code (RequestHandlerTest.java)

The should_report_no_host_when_unprepared_prepare_write_fails_synchronously test is a solid regression test for the real bug path.

One reliability concern with waitForInFlight: the assertion runs after the busy-wait loop exits, but inFlight could drop to zero between the loop condition being satisfied and the assertion. The 200 ms scassandra delay makes this practically safe, but the pattern is technically racy.

// loop exits when inFlight > 0 ...
assertThat(connection.inFlight.get()).isGreaterThan(0);  // could be 0 by now

This is unlikely to cause flakes given the 200 ms delay, but worth a note.

Comment thread driver-core/src/main/java/com/datastax/driver/core/RequestHandler.java Outdated
Comment thread driver-core/src/test/java/com/datastax/driver/core/RequestHandlerTest.java Outdated
Comment thread driver-core/src/main/java/com/datastax/driver/core/RequestHandler.java Outdated
Comment thread driver-core/src/test/java/com/datastax/driver/core/RequestHandlerTest.java Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
driver-core/src/test/java/com/datastax/driver/core/RequestHandlerTest.java (1)

25-29: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Prefer ArgumentMatchers over deprecated Matchers.

org.mockito.Matchers (Lines 25-29) has been deprecated since Mockito 2.1.0 in favor of org.mockito.ArgumentMatchers to avoid the Hamcrest name clash, and is deprecated in order to avoid a name clash with Hamcrest org.hamcrest.Matchers class, and will likely be removed in version 3.0.

♻️ Suggested import fix
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyLong;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@driver-core/src/test/java/com/datastax/driver/core/RequestHandlerTest.java`
around lines 25 - 29, The test imports in RequestHandlerTest still use
deprecated Mockito Matchers APIs; replace the org.mockito.Matchers static
imports with org.mockito.ArgumentMatchers equivalents while keeping the existing
Mockito usage in the test methods unchanged. Update the import block so any,
anyInt, and anyLong come from ArgumentMatchers, and verify no remaining
references to Matchers are left in this test class.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@driver-core/src/test/java/com/datastax/driver/core/RequestHandlerTest.java`:
- Around line 25-29: The test imports in RequestHandlerTest still use deprecated
Mockito Matchers APIs; replace the org.mockito.Matchers static imports with
org.mockito.ArgumentMatchers equivalents while keeping the existing Mockito
usage in the test methods unchanged. Update the import block so any, anyInt, and
anyLong come from ArgumentMatchers, and verify no remaining references to
Matchers are left in this test class.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 219ab2ad-9938-442b-8679-599008b196e9

📥 Commits

Reviewing files that changed from the base of the PR and between 16f5c34 and 74fe249.

📒 Files selected for processing (3)
  • driver-core/src/main/java/com/datastax/driver/core/Connection.java
  • driver-core/src/main/java/com/datastax/driver/core/RequestHandler.java
  • driver-core/src/test/java/com/datastax/driver/core/RequestHandlerTest.java

@dkropachev
dkropachev force-pushed the issue-941-requesthandler-sync-retry branch from 74fe249 to efe29bf Compare July 3, 2026 21:22
@dkropachev
dkropachev merged commit 5905a83 into scylla-3.x Jul 3, 2026
11 of 12 checks passed
@dkropachev
dkropachev deleted the issue-941-requesthandler-sync-retry branch July 3, 2026 21:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants